/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package babydisco.Math;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Stefan
 */
public class Wavelet {
    private static final double h0 = (1+ Math.sqrt(3)) / (4* Math.sqrt(2));
    private static final double h1 = (3+ Math.sqrt(3)) / (4* Math.sqrt(2));
    private static final double h2 = (3- Math.sqrt(3)) / (4* Math.sqrt(2));
    private static final double h3 = (1- Math.sqrt(3)) / (4* Math.sqrt(2));
    private static final double g0 = h3;
    private static final double g1 = -1*h2;
    private static final double g2 = h1;
    private static final double g3 = -1*h0;

    public static ArrayList<Double> waveletFilter(ArrayList<Double> input){
        ArrayList<Double>[] wavelet = d4Transform(input);
        ArrayList<Double> filtered = new ArrayList<Double>();
        
        for(int i=0; i<wavelet[0].size(); i++){
            if(i == 0){
                filtered.add(wavelet[0].get(0));
                filtered.add(wavelet[0].get(0));
            } else {
                filtered.add((wavelet[0].get(i-1)+wavelet[0].get(i))/2);
                filtered.add(wavelet[0].get(i));
            }
        }
//        if(input.size() == filtered.size())
//            System.out.println("Low-pass filter did well :D");
        return filtered;
    }
    
    //same method as above, only in- and output is Double[] instead of ArrayLists
    public static Double[] waveletFilter(Double[] input){
        ArrayList<Double> in = new ArrayList<Double>();
        for(double d: input){
            in.add(d);
        }
        
        return waveletFilter(in).toArray(new Double[0]);
    }
    
    public static ArrayList<Double> waveletD4(ArrayList<Double> input, int level){
        if(level <= 0){
            return input;
        }else if(input.size() % 2 != 0){
            System.out.println("Can't do wavelet!!!!!! Number of levels that can't be done: " + level);
            return input;
        }else{
            ArrayList<Double>[] wav = d4Transform(input);
            ArrayList<Double> w = waveletD4(wav[0], level-1);
            w.addAll(wav[1]);
            return w;
        }
        // if at level n, An is the average part of the wavelet and Dn the difference part then
        // the final wavelet will look like this: [An, Dn, Dn-1, Dn-2, ..., D2, D1]
        // so A1 = A2 + D2 (or in general An-1 = An + Dn)
    }

    private static final double UPPER_LIMIT = 0.000125;
    private static final double LOWER_LIMIT = -0.0000125;

//    public static List<Double> filter(ArrayList<Double> input, int level){
//        ArrayList<Double> wav = waveletD4(input, level);
//        ArrayList<Double> filteredWav = new ArrayList<Double>();
//        for(double d: wav){
//            if(d < UPPER_LIMIT && d > LOWER_LIMIT){
//                filteredWav.add(0.0);
//            }
//            else{
//                filteredWav.add(d);
//            }
//        }
//        return invWaveletD4(filteredWav, level);
//    }


    public static ArrayList<Double> waveletD4(ArrayList<Double> input){
        int p = getSmallerPowerOfTwo(input.size()),
                n = input.size();

        System.out.println(n + " " + p + " " + Math.pow(2,p));
        if(n > Math.pow(2,p)){
            for(int i = n-1; i >= Math.pow(2, p); i--){
                input.remove(i);
                System.out.println(i);
            }
            System.out.println(input.size());
        }

        return waveletD4(input, p);
    }

    private static int getSmallerPowerOfTwo(double a){
        int power = 0;
        while(Math.pow(2,power) < a){
            power++;
        }
        return power-1;
    }

    private static ArrayList<Double>[] d4Transform(ArrayList<Double> input){

        if(input.size()%2 >0){
            System.out.println("No Daubechies 4 transform possible...");
        }

        //a for average, w for wavelet
        ArrayList<Double> a = new ArrayList<Double>();
        ArrayList<Double> d = new ArrayList<Double>();

        for(int i =0; i<input.size(); i = i+2){
            a.add(h0*input.get(i%input.size()) + h1*input.get((i+1)%input.size()) + h2*input.get((i+2)%input.size()) + h3*input.get((i+3)%input.size()));
            d.add(g0*input.get(i%input.size()) + g1*input.get((i+1)%input.size()) + g2*input.get((i+2)%input.size()) + g3*input.get((i+3)%input.size()));
        }

        return new ArrayList[]{a,d};
    }


    public static List<Double> invWaveletD4(List<Double> wavelet, int level){
        if(level <= 0){
            return wavelet;
        }else if(level == 1){
            return(invD4Transform(wavelet));
        }else{
            List<Double> w1 = wavelet.subList(0, wavelet.size() >> 1);
            List<Double> a = invWaveletD4(w1, level-1);
            a.addAll(wavelet.subList((wavelet.size() >> 1) +1, wavelet.size()));
            return invD4Transform(a);
        }
    }

    private static ArrayList<Double> invD4Transform(List<Double> wavelet){
        ArrayList<Double> data = new ArrayList<Double>();

        if (wavelet.size() >= 4) {
            int half = wavelet.size() >> 1;


            //first the last one (I know this sounds strange)
            System.out.println(wavelet.get(half-1)*h1 + wavelet.get(0)*h3 + wavelet.get(wavelet.size()-1)*g1 + wavelet.get(half)*g3);
            data.add(wavelet.get(half-1)*h1 + wavelet.get(0)*h3 + wavelet.get(wavelet.size()-1)*g1 + wavelet.get(half)*g3);
            data.add(wavelet.get(half-1)*h0 + wavelet.get(0)*h2 + wavelet.get(wavelet.size()-1)*g0 + wavelet.get(half)*g2);

            int k = 0;
            for (int j = 2; j<wavelet.size()-2; j = j+2){
                data.add(wavelet.get(k)*h1 + wavelet.get(k+1)*h3 + wavelet.get(half+k)*g1 + wavelet.get(half+k+1)*g3);
                data.add(wavelet.get(k)*h0 + wavelet.get(k+1)*h2 + wavelet.get(half+k)*g0 + wavelet.get(half+k+1)*g2);
                k++;
            }

        } else{
            System.out.println("Inverse wavelet gone wrong!!!");
        }
        return data;
    }

    //for test purposes
//    public static void main(String[] args){
//        ArrayList<Double> data = new ArrayList<Double>();
//        data.add(1.0);
//        data.add(2.0);
//        data.add(3.0);
//        data.add(4.0);
//        data.add(1.0);
//        data.add(2.0);
//        data.add(3.0);
//        data.add(4.0);
//        data.add(1.0);
//        data.add(2.0);
//        data.add(3.0);
//        data.add(4.0);
//        data.add(1.0);
//        data.add(2.0);
//        data.add(3.0);
//        data.add(4.0);
//        data.add(3.0);
//        data.add(4.0);
//        ArrayList<Double> wav = waveletD4(data, 1);
//        for(double d: wav){
//            System.out.println("wav: " + d);
//        }
//
//        List<Double> invWav = invWaveletD4(wav, 1);
//        for(double d: invWav){
//            System.out.println("inv: " + d);
//        }
//    }
}
